package com.whfc.emp.redis.impl;

import com.whfc.common.util.DateUtil;
import com.whfc.emp.dto.AppDeviceCardLogCacheDTO;
import com.whfc.emp.dto.AppDeviceCardLogDTO;
import com.whfc.emp.entity.AppDeviceCardLog;
import com.whfc.emp.redis.CardDataRedisDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Repository;

import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: xugcheng
 * @version: 1.0
 * @date: 2020/6/2 9:59
 */
@Repository
public class CardDataRedisDaoImpl implements CardDataRedisDao {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String cardDataLogKey = "card-data-log::{0}::{1}";

    private static final int EXPIRE_HOURS = 1;

    @Autowired
    private RedisTemplate<String, AppDeviceCardLogCacheDTO> redisTemplate;

    @Override
    public void addCardDataLog(Integer empId, Date date, List<AppDeviceCardLogDTO> logList) {
        if (logList != null && logList.size() > 0) {
            Set<AppDeviceCardLogCacheDTO> logCacheSet = logList.stream().map(log -> to(log)).collect(Collectors.toSet());
            this.addCardDataLog(empId, date, logCacheSet);
        }
    }

    @Override
    public List<AppDeviceCardLog> getCardDataLog(Integer empId, Date date) {
        Set<AppDeviceCardLogCacheDTO> logCacheSet = this.getCardData(empId, date);
        return logCacheSet.stream().map(cache -> from(cache)).collect(Collectors.toList());
    }

    @Override
    public List<AppDeviceCardLog> getCardDataLog(Integer empId, Date date, Date startTime, Date endTime) {
        Set<AppDeviceCardLogCacheDTO> logCacheSet = this.getCardData(empId, date, startTime, endTime);
        return logCacheSet.stream().map(cache -> from(cache)).collect(Collectors.toList());
    }

    @Override
    public boolean exists(Integer empId, Date date) {
        String key = MessageFormat.format(cardDataLogKey, empId, DateUtil.formatDate(date));
        return redisTemplate.hasKey(key);
    }

    @Override
    public void delete(Integer empId, Date date) {
        String key = MessageFormat.format(cardDataLogKey, empId, DateUtil.formatDate(date));
        redisTemplate.delete(key);
    }

    private void addCardDataLog(Integer empId, Date date, Set<AppDeviceCardLogCacheDTO> logs) {
        String key = MessageFormat.format(cardDataLogKey, empId, DateUtil.formatDate(date));
        Set<ZSetOperations.TypedTuple<AppDeviceCardLogCacheDTO>> tuples = logs.stream()
                .map(dto -> toTypedTuple(dto))
                .collect(Collectors.toSet());
        redisTemplate.opsForZSet().add(key, tuples);
        redisTemplate.expire(key, EXPIRE_HOURS, TimeUnit.HOURS);
    }

    private Set<AppDeviceCardLogCacheDTO> getCardData(Integer empId, Date date) {
        String key = MessageFormat.format(cardDataLogKey, empId, DateUtil.formatDate(date));
        Double min = timeToScore(DateUtil.getDateBegin(date));
        Double max = timeToScore(DateUtil.getDateEnd(date));
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    private Set<AppDeviceCardLogCacheDTO> getCardData(Integer empId, Date date, Date startTime, Date endTime) {
        String key = MessageFormat.format(cardDataLogKey, empId, DateUtil.formatDate(date));
        Double min = timeToScore(startTime);
        Double max = timeToScore(endTime);
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    private ZSetOperations.TypedTuple<AppDeviceCardLogCacheDTO> toTypedTuple(AppDeviceCardLogCacheDTO dto) {
        Double score = timeToScore(dto.getTime());
        return new DefaultTypedTuple(dto, score);
    }

    private Double timeToScore(Date time) {
        return Double.valueOf(time.getTime() / 1000);
    }

    private AppDeviceCardLogCacheDTO to(AppDeviceCardLogDTO log) {
        AppDeviceCardLogCacheDTO cacheDTO = new AppDeviceCardLogCacheDTO();
        BeanUtils.copyProperties(log, cacheDTO);
        return cacheDTO;
    }

    private AppDeviceCardLog from(AppDeviceCardLogCacheDTO logCache) {
        AppDeviceCardLog log = new AppDeviceCardLog();
        BeanUtils.copyProperties(logCache, log);
        return log;
    }
}
